<?php

/*
Copyright 2009-2011 Sam Weiss
All Rights Reserved.

This file is part of Spark/Plug.

Spark/Plug is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

if (!defined('spark/plug'))
{
	header('HTTP/1.1 403 Forbidden');
	exit('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>403 Forbidden</title></head><body><h1>Forbidden</h1><p>You don\'t have permission to access the requested resource on this server.</p></body></html>');
}

// -----------------------------------------------------------------------------

class _SparkCacher_database extends SparkCacher
{
	private $_default_timeout;
	private $_gc_probability;
	private $_name_space;
	private $_table;
	private $_columns;
	private $_db;

	// --------------------------------------------------------------------------

	public function __construct($params)
	{
		parent::__construct($params);
		
		$this->_default_timeout = !empty($params['lifetime']) ? $params['lifetime'] : 31536000;
		$this->_gc_probability = !empty($params['gc_probability']) ? $params['gc_probability'] : 5;
		$this->_table = !empty($params['table']) ? $params['table'] : 'cache';
		$this->_columns = isset($params['columns']) ? $params['columns'] : array('namespace'=>'namespace', 'key'=>'ckey', 'val'=>'val', 'expires'=>'expires');
		$this->_db = $params['connection'];

		$this->setNameSpace(@$params['namespace']);
	}

	// --------------------------------------------------------------------------

	// Public Methods

	// --------------------------------------------------------------------

	public function setDefaultTimeout($timeout)
	{
		$this->_default_timeout = !empty($timeout) ? $timeout : 31536000;
	}

	// --------------------------------------------------------------------

	public function setNameSpace($namespace)
	{
		$this->_name_space = !empty($namespace) ? $namespace : NULL;
	}

	// --------------------------------------------------------------------------

	public function get($key)
	{
		try
		{
			$this->_expireItems();
			$where = $this->_db->quoteIdentifier($this->_columns['key']).'=? AND '.$this->_db->quoteIdentifier($this->_columns['expires']).'>?';
			$bind = array($key, $this->_db->getFunction('date')->now());
			if ($this->_name_space !== NULL)
			{
				$where .= ' AND '.$this->_db->quoteIdentifier($this->_columns['namespace']).'=?';
				$bind[] = $this->_name_space;
			}
			$row = $this->_db->selectRow($this->_table, $this->_db->quoteIdentifier($this->_columns['val']), $where, $bind);
			return empty($row) ? false : $row[$this->_columns['val']];
		}
		catch (Exception $e)
		{
			return false;
		}
	}

	// --------------------------------------------------------------------------

	public function set($key, $item, $timeout = NULL)
	{
		if ($timeout === 0)
		{
			$timeout = 31536000;		// forever? one year is plenty long!
		}
		$expires = $this->_db->getFunction('date')->now()->interval($timeout !== NULL ? $timeout : $this->_default_timeout, iSparkDBQueryFunctionDate::kSeconds)->add();
		$row = array($this->_columns['key']=>$key, $this->_columns['val']=>array($item), $this->_columns['expires']=>$expires);
		if ($this->_name_space !== NULL)
		{
			$row[$this->_columns['namespace']] = $this->_name_space;
		}
		try
		{
			return @$this->_db->insertRow($this->_table, $row);
		}
		catch (Exception $e)
		{
		}

		$where = $this->_db->quoteIdentifier($this->_columns['key']).'=?';
		$bind = array($key);
		if ($this->_name_space !== NULL)
		{
			$where .= ' AND '.$this->_db->quoteIdentifier($this->_columns['namespace']).'=?';
			$bind[] = $this->_name_space;
		}
		try
		{
			return $this->_db->updateRows($this->_table, array($this->_columns['val']=>array($item), $this->_columns['expires']=>$expires), $where, $bind);
		}
		catch (Exception $e)
		{
			return false;
		}
	}

	// --------------------------------------------------------------------------

	public function replace($key, $item, $timeout = NULL)
	{
		if ($timeout === 0)
		{
			$timeout = 31536000;		// forever? one year is plenty long!
		}
		$expires = $this->_db->getFunction('date')->now()->interval($timeout !== NULL ? $timeout : $this->_default_timeout, iSparkDBQueryFunctionDate::kSeconds)->add();
		$where = $this->_db->quoteIdentifier($this->_columns['key']).'=?';
		$bind = array($key);
		if ($this->_name_space !== NULL)
		{
			$where .= ' AND '.$this->_db->quoteIdentifier($this->_columns['namespace']).'=?';
			$bind[] = $this->_name_space;
		}
		try
		{
			return $this->_db->updateRows($this->_table, array($this->_columns['val']=>array($item), $this->_columns['expires']=>$expires), $where, $bind);
		}
		catch (Exception $e)
		{
			return false;
		}
	}

	// --------------------------------------------------------------------------

	public function remove($key)
	{
		$where = $this->_db->quoteIdentifier($this->_columns['key']).'=?';
		$bind = array($key);
		if ($this->_name_space !== NULL)
		{
			$where .= ' AND '.$this->_db->quoteIdentifier($this->_columns['namespace']).'=?';
			$bind[] = $this->_name_space;
		}
		try
		{
			return $this->_db->deleteRow($this->_table, $where, $bind);
		}
		catch (Exception $e)
		{
			return false;
		}
	}

	// --------------------------------------------------------------------------

	public function clear()
	{
		if ($this->_name_space !== NULL)
		{
			$where = $this->_db->quoteIdentifier($this->_columns['namespace']).'=?';
			$bind = array($this->_name_space);
		}
		else
		{
			$where = NULL;
			$bind = NULL;
		}
		try
		{
			return $this->_db->deleteRows($this->_table, $where, $bind);
		}
		catch (Exception $e)
		{
			return false;
		}
	}

	// --------------------------------------------------------------------------

	// Private Methods

	// --------------------------------------------------------------------------
	
	private function _expireItems()
	{
		if (mt_rand(1,100) <= $this->_gc_probability)
		{
			$where = $this->_db->quoteIdentifier($this->_columns['expires']).'<=?';
			$bind = array($this->_db->getFunction('date')->now());
			if ($this->_name_space !== NULL)
			{
				$where .= ' AND '.$this->_db->quoteIdentifier($this->_columns['namespace']).'=?';
				$bind[] = $this->_name_space;
			}
			$this->_db->deleteRows($this->_table, $where, $bind);
		}
	}

	// --------------------------------------------------------------------------
}
